#include "MGUI_FontManager.h"

#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftbitmap.h>

namespace Myway {

#define FONT_TEXTURE_SIZE 256

	FT_Library gFTLibrary;


	MGUI_Font::MGUI_Font(const TString128 & _name, void * _face, int _charSize)
		: mName(_name)
		, mFace(_face)
		, mCharSize(_charSize)
		, _texture(NULL)
		, _x(0)
		, _y(0)
		, _height(0)
	{
		FT_Face ftFace = (FT_Face)mFace;

		mAscender = 0;
		mDescender = 0;
		mHeight = 0;

		if (FT_Set_Pixel_Sizes(ftFace, mCharSize, mCharSize) == 0)
		{
			mAscender = float(ftFace->size->metrics.ascender >> 6);
			mDescender = float(ftFace->size->metrics.descender >> 6);
			mHeight = float(ftFace->size->metrics.height >> 6);
		}
	}

	MGUI_Font::~MGUI_Font()
	{
		for (int i = 0; i < mTextures.Size(); ++i)
		{
			mTextures[i] = NULL;
		}

		mTextures.Clear();
		mGlyphs.Clear();
	}

	const MGUI_Glyph * MGUI_Font::GetGlyph(MGUI_Char _char)
	{
		HashMap<MGUI_Char, MGUI_Glyph>::Iterator whr = mGlyphs.Find(_char);
		HashMap<MGUI_Char, MGUI_Glyph>::Iterator end = mGlyphs.End();

		if (whr != end)
			return &(whr->second);

		MGUI_Glyph glyph;

		if (_LoadGlyph(glyph, _char))
		{
			whr = mGlyphs.Insert(_char, glyph);

			return &(whr->second);
		}

		return NULL;
	}

	bool MGUI_Font::_LoadGlyph(MGUI_Glyph & _glyph, MGUI_Char _char)
	{
		FT_Face ftFace = (FT_Face)mFace;

		if (FT_Set_Pixel_Sizes(ftFace, mCharSize, mCharSize) != 0)
			return false;

		if (FT_Load_Char(ftFace, _char, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL) != 0)
			return false;

		int bw = ftFace->glyph->bitmap.width;
		int bh = ftFace->glyph->bitmap.rows;

		_IncTexture(bw, bh);

		_glyph.bearingX = float(ftFace->glyph->metrics.horiBearingX >> 6);
		_glyph.bearingY = mAscender - float(ftFace->glyph->metrics.horiBearingY >> 6);
		_glyph.advance = float(ftFace->glyph->advance.x >> 6);
		_glyph.texture = _texture;

		LockedRect rect;
		_texture->Lock(&rect, LK_READ | LK_WRITE);
		{
			if (ftFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
			{
				unsigned int * dest = (unsigned int *)rect.pData + FONT_TEXTURE_SIZE * _y + _x;
				unsigned char * src = (unsigned char*)ftFace->glyph->bitmap.buffer;

				for (int j = 0; j < bh; ++j)
				{
					for (int i = 0; i < bw; ++i)
					{
						int val = int(src[i]);

						dest[i] = M_RGBA(0xFF, 0xFF, 0xFF, val); 
					}

					src += ftFace->glyph->bitmap.pitch;
					dest += FONT_TEXTURE_SIZE;
				}
			}
			else if (ftFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
			{
				FT_Bitmap ftBitmap;
				FT_Bitmap_New(&ftBitmap);

				// Convert the monochrome bitmap to 8-bit before rendering it.
				if (FT_Bitmap_Convert(gFTLibrary, &ftFace->glyph->bitmap, &ftBitmap, 1) == 0)
				{
					// Go through the bitmap and convert all of the nonzero values to 0xFF (white).
					for (unsigned char * p = ftBitmap.buffer, * endP = p + ftBitmap.width * ftBitmap.rows; p != endP; ++p)
						*p ^= -*p ^ *p;
				}

				unsigned int * dest = (unsigned int *)rect.pData + FONT_TEXTURE_SIZE * _y + _x;
				unsigned char * src = (unsigned char*)ftBitmap.buffer;

				for (int j = 0; j < bh; ++j)
				{
					for (int i = 0; i < bw; ++i)
					{
						int val = int(src[i]);

						dest[i] = M_RGBA(0xFF, 0xFF, 0xFF, val); 
					}

					src += ftBitmap.pitch;
					dest += FONT_TEXTURE_SIZE;
				}

				FT_Bitmap_Done(gFTLibrary, &ftBitmap);
			}
		}
		_texture->Unlock();

		_glyph.width = float(ftFace->glyph->metrics.width >> 6);
		_glyph.height = float(ftFace->glyph->metrics.height >> 6);
		_glyph.texture = _texture;

		static const float InvSize = 1.0f / float(FONT_TEXTURE_SIZE);

		_glyph.u0 = (_x) * InvSize;
		_glyph.v0 = (_y) * InvSize;
		_glyph.u1 = (_x + bw) * InvSize;
		_glyph.v1 = (_y + bh) * InvSize;

		_x += bw + 1;

		if (bh > _height)
			_height = bh;

		return true;
	}

	void MGUI_Font::_IncTexture(int bw, int bh)
	{
		bool needCreateTexture = false;

		if (_texture)
		{
			if (_x + bw + 1 >= FONT_TEXTURE_SIZE)
			{
				_y += _height + 1;
				_x = 1;
				_height = 0;
			}

			if (_y >= FONT_TEXTURE_SIZE)
			{
				needCreateTexture = true;
			}
		}
		else
		{
			needCreateTexture = true;
		}

		if (needCreateTexture)
		{
			static int UID = 0;

			TexturePtr t = TextureManager::Instance()->CreateTexture(TString128("MGUI_FontTexture_") + UID++, FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE, 0);

			LockedRect rect;
			t->Lock(&rect, LK_READ | LK_WRITE);
			{
				unsigned int * pixel = (unsigned int *)rect.pData;

				for (int j = 0; j < FONT_TEXTURE_SIZE; ++j)
				{
					for (int i = 0; i < FONT_TEXTURE_SIZE; ++i)
					{
						*pixel++ = 0x00FFFFFF;
					}
				}
			}
			t->Unlock();

			_texture = t.c_ptr();
			mTextures.PushBack(_texture);

			_y = 1;
			_x = 1;
		}
	}





	MGUI_FontManager * MGUI_FontManager::msInstance = NULL;

	MGUI_FontManager::MGUI_FontManager()
	{
		msInstance = this;

		FT_Init_FreeType(&gFTLibrary);
	}

	MGUI_FontManager::~MGUI_FontManager()
	{
		for (int i = 0; i < mFonts.Size(); ++i)
		{
			delete mFonts[i];
		}

		mFonts.Clear();

		Map<TString128, void *>::Iterator whr = mFaceMap.Begin();
		Map<TString128, void *>::Iterator end = mFaceMap.End();

		while (whr != end)
		{
			FT_Face face = (FT_Face)whr->second;

			FT_Done_Face(face);

			++whr;
		}

		mFaceMap.Clear();

		for (int i = 0; i < mFaceData.Size(); ++i)
		{
			mFaceData[i] = NULL;
		}

		mFaceData.Clear();

		FT_Done_FreeType(gFTLibrary);

		msInstance = NULL;
	}

	void MGUI_FontManager::Load(const TString128 & _source)
	{
		DataStreamPtr stream = ResourceManager::Instance()->OpenResource(_source.c_str());

		if (stream == NULL)
		{
			LOG_PRINT_FORMAT("Error: Source '%s' can't opened.\n", _source.c_str());
			return ;
		}

		xml_doc doc;
		XmlHelper::LoadXmlFromMem(doc, (char *)stream->GetData());

		xml_node * root = doc.first_node();
		xml_node * node = root->first_node("Font");

		while (node)
		{
			TString128 name = node->first_attribute("name")->value();
			TString128 face = node->first_attribute("face")->value();
			TString128 size = node->first_attribute("size")->value();

			d_assert(GetFont(name) == NULL);

			void * ftFace = _loadFace(face);

			if (ftFace)
			{
				MGUI_Font * font = new MGUI_Font(name, ftFace, size.ToInt());

				mFonts.PushBack(font);
			}

			node = node->next_sibling("Font");
		}
	}

	void * MGUI_FontManager::_loadFace(const TString128 & _face)
	{
		Map<TString128, void *>::Iterator whr = mFaceMap.Find(_face);
		Map<TString128, void *>::Iterator end = mFaceMap.End();

		if (whr != end)
			return whr->second;

		DataStreamPtr stream = ResourceManager::Instance()->OpenResource(_face.c_str());

		if (stream == NULL)
		{
			LOG_PRINT_FORMAT("Error: Font face '%s' can't open!\n", _face.c_str());

			return NULL;
		}

		DataStreamPtr faceData = new MemoryStream(stream->Size());

		stream->Read(faceData->GetData(), stream->Size());

		FT_Face ftFace = NULL;
		if (FT_New_Memory_Face(gFTLibrary, (const FT_Byte*)faceData->GetData(), stream->Size(), 0, &ftFace) != 0)
			return NULL;

		mFaceMap.Insert(_face, ftFace);
		mFaceData.PushBack(faceData);

		return ftFace;
	}

	MGUI_Font * MGUI_FontManager::GetFont(const TString128 & _name)
	{
		for (int i = 0; i < mFonts.Size(); ++i)
		{
			if (mFonts[i]->Name() == _name)
				return mFonts[i];
		}

		return NULL;
	}

	MGUI_Font * MGUI_FontManager::GetDefaultFont()
	{
		if (mFonts.Size())
			return mFonts[0];

		return NULL;
	}

}

